package com.alibaba.japan.club.runtime.springmvc.component;

import java.io.IOException;

import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.servlet.mvc.Controller;

import com.alibaba.japan.club.runtime.springmvc.module.ModuleHelper;
import com.alibaba.japan.club.runtime.springmvc.module.ModulePattern;
import com.alibaba.japan.club.runtime.springmvc.servlet.view.freemarker.FreeMarkerScreen;

/***
 * Spring扫描类
 * 
 * @author shaochuan.wangsc
 * @date 2009-3-24
 * 
 */
public class JapanBeanScanner implements ApplicationContextAware, BeanClassLoaderAware, ResourceLoaderAware,
		InitializingBean {

	private static final Logger logger = Logger.getLogger(JapanBeanScanner.class);

	private static final String RESOURCE_PATTERN = "**/*.class";
	
	private ModulePattern modulePattern;

	private boolean alreadyInit = false;

	private XmlWebApplicationContext applicationContext;

	private ClassLoader classLoader;

	private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

	public void setModulePattern(ModulePattern modulePattern) {
		this.modulePattern = modulePattern;
	}

	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		if (alreadyInit == false) {
			this.applicationContext = (XmlWebApplicationContext) applicationContext;
			alreadyInit = true;
		}
	}

	public void setBeanClassLoader(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}

	public void setResourceLoader(ResourceLoader resourceLoader) {
		this.resourcePatternResolver = (resourceLoader != null ? ResourcePatternUtils
				.getResourcePatternResolver(resourceLoader) : new PathMatchingResourcePatternResolver());
	}

	public void afterPropertiesSet() throws Exception {
		String path = modulePattern.getPackagePath();
		scanPackage(path);
	}

	/**
	 * 进行包扫描
	 * 
	 * @param path
	 */
	private void scanPackage(String path) {
		try {
			String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(path) + "/" + RESOURCE_PATTERN;
			Resource[] resources = this.resourcePatternResolver.getResources(pattern);
			MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
			for (Resource resource : resources) {
				if (resource.isReadable()) {
					MetadataReader reader = readerFactory.getMetadataReader(resource);
					ClassMetadata classMetadata = reader.getClassMetadata();
					if (classMetadata.isConcrete()) {
						registerClass(classMetadata.getClassName());
					}
				}
			}
		} catch (IOException e) {
			logger.error("Failed to scan classpath for unlisted classes", e);
			throw new RuntimeException("Failed to scan classpath for unlisted classes", e);
		} catch (ClassNotFoundException e) {
			logger.error("Failed to load classes from classpath", e);
			throw new RuntimeException("Failed to load classes from classpath", e);
		} catch (InstantiationException e) {
			logger.error("Failed to load classes from classpath", e);
			throw new RuntimeException("Failed to load classes from classpath", e);
		} catch (IllegalAccessException e) {
			logger.error("Failed to load classes from classpath", e);
			throw new RuntimeException("Failed to load classes from classpath", e);
		}
	}

	/**
	 * 注册class到spring中
	 * 
	 * @param className
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
	private void registerClass(String className) throws ClassNotFoundException, InstantiationException,
			IllegalAccessException {
		if (!(ModuleHelper.isScreenBean(className) || ModuleHelper.isControllerBean(className))) {
			return;
		}
		ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
		Object clazz = beanFactory.createBean(classLoader.loadClass(className), AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT, false);
		if (clazz instanceof FreeMarkerScreen) {
			String screenBeanName = ModuleHelper.makeScreenBeanName(modulePattern, className);
			if (!"".equals(screenBeanName)) {
				modulePattern.getScreenMapping().put(screenBeanName, clazz);
			}
		}
		if (clazz instanceof Controller) {
			String controlBeanName = ModuleHelper.makeControlBeanName(modulePattern, className);
			String mappingKey = ModuleHelper.makeUrlMappingKey(modulePattern, controlBeanName);
			if (!"".equals(mappingKey) && !"".equals(controlBeanName)) {
				modulePattern.getUrlMapping().put(mappingKey, clazz);
			}
		}
	}

}
